
module Tablecon

	class Table 
		##
		# Params:
		# +list+:: a list of instances of the same class 
		# 				where every field will be treated as a 
		# 				separate column 
		def initialize list
			lp = ListParser.new list
			@printer = TablePrinter.new lp.rows
		end

		##
		# prints @list as a table
		def to_s
			@printer.str
		end
	end

	class TablePrinter
		def initialize rows
			@rows = rows
			raise "Rows are empty!" if @rows.empty?
		end
		
		def str
			ret = ""
			ret << header
			@rows.each do |row|
				row.each do |cell|
					ret << "|"
					ret << cell.value.to_s.center(cell.width + 2)
				end
				ret << "|\n"
				ret << delimiter	
			end
			ret
		end

		private 
		def header
			str = ""
			# line 1
			str << delimiter
			# line 2
			@rows.first.each do |cell|
				str << "|"
				str << cell.column.name.to_s.center(cell.width + 2)
			end
			str << "|\n"
			# line 3
			str << delimiter
		end

		def delimiter
			str = ""
			@rows.first.each do |cell|
				str << "+"
				str << "-".center(cell.width + 2, "-")
			end
			str << "+\n"
		end

	end

	class ListParser
		attr_accessor :columns, :rows

		def initialize list
			@columns = {}
			@rows = [] 
			parse list
		end

		private

		##
		# parse list and fill in rows and columns
		def parse list
			raise "list is empty!" if list.empty?
			first = list[0]
			init_columns first
			
			list.each do |object|
				vars = object.instance_variables

				if vars != first.instance_variables
					raise "list not consistent, expect to have the following columns: " 
					+ first.instance_variables + " but got " + vars
				end

				row = Row.new
				@rows << row
				vars.each do |var|
					value = "#{object.instance_variable_get var}"
					col = @columns[var]
					raise "Cannot find column for #{col}" unless col
					row << Cell.new(col, value)
					col.length = value.length if value.length > col.length
				end
			end
		end
		
		##
		# populate only the names
		def init_columns object
			object.instance_variables.each do |var|
				name = var.to_s[1..-1].capitalize
				@columns[var] = Column.new name, name.length
			end
		end
	end

	class Cell
		attr_accessor :column, :value
		def initialize column, value
			@column = column
			@value = value
		end
		def width
			@column.length
		end
	end

	class Column
		attr_accessor :name, :length
		def initialize name, length
			@name = name
			@length = length
		end
		def == col
			@name == col.name and @length == col.length 
		end
		def to_s
			"Column[name=#{@name} length=#{@length}]"
		end
	end

	class Row
		def initialize 
			@cells = []
		end
		def << cell
			@cells << cell
		end
		def each
			@cells.each { |cell| yield cell }
		end
	end
end
